package server

import (
	"log"
	"net/http"
	"server/framework/web/context"
	"server/framework/web/handler"
	middleware2 "server/framework/web/middleware"
	template2 "server/framework/web/template/engine"
	"sync"
)

type Server interface {
	handler.Routable
	Start(address string) error
	Shutdown(c *context.Context) error
}

type SdkHttpServer struct {
	name        string
	handler     handler.Handler
	middlewares []middleware2.Middleware
	context     sync.Pool
	engine      template2.Engine
}

func (s *SdkHttpServer) Shutdown(c *context.Context) error {
	log.Printf("shutting down server： %s\n", s.name)
	return nil
}

// Route 路由注册
func (s *SdkHttpServer) Route(method string, pattern string, handleFunc middleware2.HandleFunc) {
	s.handler.Route(method, pattern, handleFunc)
}

func (s *SdkHttpServer) Start(address string) error {
	root := s.handler.ServeHTTP

	root = middleware2.FlashResp(root)

	for _, m := range s.middlewares {
		root = m(root)
	}

	return http.ListenAndServe(address, http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		c := s.context.Get()
		ctx := c.(*context.Context)
		ctx.Reset(writer, request)
		root(ctx)
		defer func() {
			s.context.Put(ctx)
		}()
	}))
}

var _ Server = &SdkHttpServer{}

type SdkHttpOpt func(s *SdkHttpServer)

func WithTemplate(engine template2.Engine) SdkHttpOpt {
	return func(s *SdkHttpServer) {
		s.engine = engine
	}
}

type HttpServerConfig struct {
	Name        string
	Handler     handler.Handler
	Middlewares []middleware2.Middleware
	Context     *context.Context
}

func NewSdkHttpServer(serverConfig HttpServerConfig, opts ...SdkHttpOpt) *SdkHttpServer {
	s := &SdkHttpServer{
		name:        serverConfig.Name,
		handler:     serverConfig.Handler,
		middlewares: serverConfig.Middlewares,
	}
	for _, opt := range opts {
		opt(s)
	}

	if serverConfig.Context == nil {

	}

	if s.engine != nil {
		s.context = sync.Pool{New: func() interface{} {
			return &context.Context{
				Engine: s.engine,
			}
		}}
	} else {
		s.context = sync.Pool{New: func() interface{} {
			return &context.Context{}
		}}
	}
	return s
}
